package us.mmapp.util;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.auth.BasicScheme;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import us.mmapp.entities.FriendEntity;
import us.mmapp.entities.ObjectEntity;
import us.mmapp.interfaces.OnApplicationDataChangeListener;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;

public class ApplicationData
{
	private ApplicationData()
	{}
	
	private static int[] mSelection;
	private static String[] mKeyword;
	
	private static ArrayList<ObjectEntity> mObjects;
	private static HashMap<Long, FriendEntity> mFriends;
	
	private static String mUsername;
	private static String mPassword;
	private static long mUid;
	private static boolean isLoggedIn;
	private static Context mContext;
	
	private static boolean isRefreshing = false;
	
	public static void setContext(Context context)
	{
		mContext = context;
	}
	public static void setObjects(ArrayList<ObjectEntity> mObjects)
	{
		ApplicationData.mObjects = mObjects;
		notifyAllListeners();
	}
	
	public static ArrayList<ObjectEntity> getObjects()
	{
		// Objects lost, refresh.
		if (mObjects == null)
			refresh();
		return mObjects;
	}
	public static HashMap<Long, FriendEntity> getFriends()
	{
		return mFriends;
	}
	private static ArrayList<OnApplicationDataChangeListener> listeners;
	
	public static void attach(OnApplicationDataChangeListener listener)
	{
		if (listeners == null)
			listeners = new ArrayList<OnApplicationDataChangeListener>();
		listeners.add(listener);
	}
	
	public static void deattach(OnApplicationDataChangeListener listener)
	{
		if (listeners != null && listeners.contains(listener))
			listeners.remove(listener);
	}
	
	private static void notifyAllListeners()
	{
		Log.v("Application Data", "Notifying all about new data: " + mObjects.size());
		if (listeners != null)
		for (OnApplicationDataChangeListener l : listeners)
		{
				l.onApplicationDataChange();
		}
	}
	
	private static JSONObject fetchObjects(String...args)
	{
		JSONObject ret = null;
		try
		{
	    	HttpGet get = new HttpGet(ServicesUtils.GET_ALL);
	    	
	    	get.addHeader(BasicScheme.authenticate(
	    			new UsernamePasswordCredentials(args[0],TextUtils.md5(args[1])),
	    			"UTF-8",
	    			false));
	    	
	    	HttpResponse httpResponse = HttpManager.execute(get);
	    	HttpEntity responseEntity = httpResponse.getEntity();
	    	
            if (responseEntity != null)
            {
                InputStream instream = responseEntity.getContent();
                String result = TextUtils.convertStreamToString(instream);           
                instream.close();
                // Try converting result to JSON
                try {
					ret = new JSONObject(result);
				} catch (JSONException e) {
					ret = null;
					Log.e("JSON: ", e.getMessage());
				}
            }
		}
		catch (IOException e)
		{}
		return ret;
	}
	private static JSONArray fetchFriends(String...args)
	{
		JSONArray ret = null;
		try
		{
	    	HttpGet get = new HttpGet(ServicesUtils.GET_FRIENDS);
	    	
	    	get.addHeader(BasicScheme.authenticate(
	    			new UsernamePasswordCredentials(args[0],TextUtils.md5(args[1])),
	    			"UTF-8",
	    			false));
	    	
	    	HttpResponse httpResponse = HttpManager.execute(get);
	    	HttpEntity responseEntity = httpResponse.getEntity();
	    	
            if (responseEntity != null)
            {
                InputStream instream = responseEntity.getContent();
                String result = TextUtils.convertStreamToString(instream);           
                instream.close();
                // Try converting result to JSON
                Log.e("FRIENDS", "Result: " + httpResponse);
                try {
					ret = new JSONArray(result);
				} catch (JSONException e) {
					Log.e("FRIENDS", "Could not parse JSON.");
					ret = null;
				}
            }
            else
            {
            	Log.e("FRIENDS", "Response Entity Null.");
            }
		}
		catch (IOException e)
		{}
    	
		return ret;
	}
	/**
	 * Process Objects results, and add them to the mObjects.
	 * @param result
	 */
	private static void processObjects(JSONObject result)
	{
		JSONArray resultArray = null;
		try
		{
			resultArray = result.getJSONArray("result");
		} catch (JSONException e) {}
		
		if (resultArray == null)
			return;
		//resultArray JSONARRAY
		
		for (int i = 0; i < resultArray.length(); ++i)
		{
			JSONObject obj = null;
			try
			{
				obj = resultArray.getJSONObject(i);
				//Add to database
				ObjectEntity oe = new ObjectEntity(obj);
				oe.insertDB(mContext);
			} catch (JSONException e) {}
			if (obj == null)
				continue;
		}
	}
	private static void processFriends(JSONArray result)
	{
		if (mFriends == null)
			mFriends = new HashMap<Long, FriendEntity>();
		for (int i = 0; i < result.length(); ++i)
		{
			try
			{
				FriendEntity friend = new FriendEntity(result.getJSONObject(i));
				mFriends.put(friend.getUid(), friend);
			} catch (JSONException e)
			{
				e.printStackTrace();
			}
		}
	}
	public static void refresh()
	{
		if (isRefreshing) return;
		if (!isLoggedIn) return;
		isRefreshing = true;
		Log.v("Application Data", "Refresh called.");
		//parameters, the progress values, and the final value of the task,
		AsyncTask<String, Void, Object[]> t = new AsyncTask<String, Void, Object[]>() {

			@Override
			protected Object[] doInBackground(String... args) {
		    	Object[] result = new Object[2];
		    	
		    	result[0] = fetchObjects(args);
				result[1] = fetchFriends(args);
				
				mObjects = new ArrayList<ObjectEntity>();
				
				if (mFriends == null)
					mFriends = new HashMap<Long, FriendEntity>();
				
				if (result == null)
				{
					Log.e("Application Data", "NULL RESULTS");
				}
				else
				{
					//RESULT 1 which are the friends
					if (result[1] == null)
					{
						Log.e("Application Data", "NULL RESULTS FOR FRIENDS");
					}
					else
					{
						processFriends((JSONArray) result[1]);
					}	
					
					// RESULT 0 which are the objects
					if (result[0] == null)
					{
						Log.e("Application Data", "NULL RESULTS FOR OBJECTS");
					}
					else
					{
						try {
							// Test if the error was set.
							((JSONObject) result[0]).get("error");
							Log.e("Application Data", "Error with objects: " + ((JSONObject) result[0]).get("error"));
						} catch (JSONException e)
						{
							processObjects((JSONObject) result[0]);
							mObjects = ObjectEntity.getAllObjects(mContext);
						}
					}
					
				}
				
				return result;
			}
			@Override
			protected void onPostExecute(Object[] result) {
				
				// notify all
				notifyAllListeners();
				isRefreshing = false;
			}				
		};
		// Args needed for the working thread.
		String[] args = {mUsername, mPassword};
		t.execute(args);
	}
	
	/**
	 * Log out. Wipe the data.
	 */
	public static void logOut()
	{
		Log.v("Application Data", "Log out announced.");
		mSelection=null;
		mKeyword=null;
		mPassword = mUsername = "";
		mUid = 0;
		mObjects = null;
		mFriends = null;
		isLoggedIn = false;
	}
	/**
	 * Log in. and refresh the data.
	 * @param password
	 * @param username
	 * @param uid
	 */
	public static void logIn(String password, String username, long uid)
	{
		Log.v("Application Data", "Log in announced: " + username);
		mPassword = password;
		mUsername = username;
		mUid = uid;
		isLoggedIn = true;
		refresh();
	}
	public static boolean getIsLoggedIn()
	{
		return isLoggedIn;
	}
	public static String getUsername()
	{
		return mUsername;
	}
	public static String getPassword()
	{
		return mPassword;
	}
	public static long getUserId()
	{
		return mUid;
	}
	public static int[] getSelection()
	{
		return mSelection;
	}
	public static void setSelection(int[] mSelection)
	{
		ApplicationData.mSelection = mSelection;
	}
	public static String[] getKeyword()
	{
		return mKeyword;
	}
	public static void setKeyword(String[] mKeyword)
	{
		ApplicationData.mKeyword = mKeyword;
	}
	
}
